package com.zeng.webmvc;

import com.zeng.webmvc.controller.Controller;
import com.zeng.webmvc.controller.PageController;
import com.zeng.webmvc.controller.RestController;
import com.zeng.webmvc.threadlocal.ConfigThreadLocal;
import org.apache.commons.lang.ArrayUtils;
import org.eclipse.microprofile.config.Config;

import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Path;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author zyb
 * @date 2021-03-02 21:56
 */
public class DispatcherControllerServlet extends HttpServlet {

    private Map<String, HandlerMethodInfo> pathHandlerMethodInfoMap = new HashMap<>();

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        initHandlerMethodInfo();
    }

    /**
     * 读取 controller 的请求方法存储
     */
    private void initHandlerMethodInfo() {
        for (Controller controller : ServiceLoader.load(Controller.class)) {
            Class<? extends Controller> clazz = controller.getClass();
            String path = "";
            Path prefixPath = clazz.getAnnotation(Path.class);
            if (prefixPath != null) {
                path += prefixPath.value();
            }

            for (Method method : clazz.getMethods()) {
                String requestPath = path;
                Set<String> httpMethods = findSupportMethods(method);
                Path methodPath = method.getAnnotation(Path.class);
                if (methodPath != null) {
                    requestPath += methodPath.value();
                }
                HandlerMethodInfo handlerMethodInfo = new HandlerMethodInfo(requestPath, httpMethods, controller, method);
                pathHandlerMethodInfoMap.put(requestPath, handlerMethodInfo);
            }

        }
    }

    /**
     * 查询支持的http method
     *
     * @param method
     * @return
     */
    private Set<String> findSupportMethods(Method method) {
        Set<String> httpMethods = new HashSet<>();
        HttpMethod[] methods = method.getAnnotationsByType(HttpMethod.class);
        if (ArrayUtils.isNotEmpty(methods)) {
            Arrays.stream(methods).map(HttpMethod::value).forEach(httpMethods::add);
        }
        if (httpMethods.isEmpty()) {
            httpMethods.addAll(Arrays.asList(HttpMethod.GET, HttpMethod.POST,
                    HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.OPTIONS));
        }
        return httpMethods;
    }


    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = request.getServletContext();
        String requestURI = request.getRequestURI();
        HandlerMethodInfo handlerMethodInfo = pathHandlerMethodInfoMap.get(requestURI);
        if (handlerMethodInfo == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        Set<String> httpMethods = handlerMethodInfo.getHttpMethods();
        if (!httpMethods.contains(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            return;
        }
        Controller controller = handlerMethodInfo.getController();
        if (controller instanceof PageController) {
            PageController pageController = (PageController) controller;
            String viewPath = pageController.execute(request, response);
            if (!viewPath.startsWith("/")) {
                viewPath = "/" + viewPath;
            }
            RequestDispatcher dispatcher = servletContext.getRequestDispatcher(viewPath);
            dispatcher.forward(request, response);
            return;
        }
        if (controller instanceof RestController) {
            Method method = handlerMethodInfo.getMethod();
            try {
                method.invoke(controller, request, response);
            } catch (Exception e) {
                throw new ServletException(e);
            }
        }

    }

    @Override
    public void destroy() {

    }
}
